#include "bsp_key.h"
#include <stdio.h>
#include <stdarg.h>
#include "gpio.h"
#include "FreeRTOS.h"
#include "cmsis_os.h"
#include "usart.h"
#include "queue.h"
#include "task.h"

// #include "bsp_uart.h"
QueueHandle_t gSWQueueHandle = 0;   // 按键事件队列句柄
extern osMutexId usartMutexHandle; // 串口互斥锁句柄
int uart_printf(const char *fmt, ...);

struct sw_event
{
    uint8_t id;
    uint8_t event;
};
union sw_event_info
{
    uint16_t data;
    struct sw_event s;
};
enum sw_id
{
    SW1_ID = 0,
};

// 调试打印
#define debugPrint(str, ...) uart_printf(str, ##__VA_ARGS__)
// 按键输入电平读取
#define Bsp_Key_ReadPin(Port, Pin) HAL_GPIO_ReadPin(Port, Pin)
/* [1] 创建按键对象 */
iSW_t hisw1[BSP_KEY_NUM] = {0};

/* [2] 实现按键电平读取接口 */
void bsp_key_input_read(uint8_t offset, uint8_t *results, uint16_t num)
{
    if (results == NULL || BSP_KEY_NUM - offset < num)
    {
        debugPrint("Error: bsp_key_input_read\n");
        return ;
    }
    uint16_t i = 0;
    switch (offset) // 偏移值用来方便读取单个按键
    {
    case 0:
        if (num > i)
            results[i++] = Bsp_Key_ReadPin(KEY0_GPIO_Port, KEY0_Pin);
        else break;
    default:
        break;
    }
}

/* ---[3] 初始按键并配置功能--- */
void bsp_key_init(void)
{
    // 初始化按键 设置触发为低电平，消抖时间为20ms
    iSW_Init(hisw1, BSP_KEY_NUM, iSW_TRIGGER_LEVEL_LOW, 20);
    // 设置按键3为模式2，长按时间为2s，短按时间为20ms 触发方式为松开触发，双击时间为40ms~250ms
    iSW_Set_Mode2(&hisw1[0], 2000, 20, iSW_TRIGGER_WAY_RELEASE);    
    iSW_Set_Double_Click(&hisw1[0], 40, 250);

}

void bsp_queue_init(void)
{
    gSWQueueHandle = xQueueGenericCreate(sizeof(union sw_event_info), 5, 0);
    if (gSWQueueHandle == NULL)
    {
        vTaskSuspend(NULL);
    }
}


void TaskInput(void const * argument)
{
    uint8_t key_input[BSP_KEY_NUM];
    bsp_key_init(); // 初始化按键设置
    debugPrint("TaskInput start\n\r");
    if (0 == gSWQueueHandle) osDelay(10);
    while(1)
    {
        union sw_event_info swevent;
        /* [4] 在任务或定时器(1~20ms)中读取按键输入数据，并使用iSW_Scan来扫描输入数据 */
        bsp_key_input_read(0, key_input, BSP_KEY_NUM);
        if (iSW_Scan(hisw1, key_input, BSP_KEY_NUM, 10)) // 10ms扫描一次
        {
            /* [5] 按键事件处理 */
            // 独立判断每个按键的事件
            swevent.s.event = iSW_EVENT_GET(&hisw1[0], iSW_EVENT_ALL);
            if (swevent.s.event)
            {
                swevent.s.id = 0;
                xQueueSend(gSWQueueHandle, &swevent.data, 5);
            }

        }
        // 清空按键事件
        iSW_Clear(hisw1, BSP_KEY_NUM);
        // 延时10ms
        osDelay(10);
    }
}

static void SW_Handler(const struct sw_event *e)
{
    switch (e->id)
    {
    case SW1_ID:
        if (e->event & iSW_EVENT_PRESS)
        {
            debugPrint("KEY0 press start\n\r");
        }
        if (e->event & iSW_EVENT_SHORT)
        {
            HAL_GPIO_TogglePin(LED0_GPIO_Port, LED0_Pin);
            debugPrint("KEY0 short press\n\r");
        }
        if (e->event & iSW_EVENT_LONG)
        {
            debugPrint("KEY0 long press\n\r");
        }
        if (e->event & iSW_EVENT_DOUBLE_CLICK)
        {
            debugPrint("KEY0 double click\n\r");
        }
        if (e->event & iSW_EVENT_RELEASE)
        {
            debugPrint("KEY0 release\n\r");
        }
        break;
    default:
        break;
    }
}

void TaskLED(void const *argument)
{
    BaseType_t status;
    union sw_event_info swevent;
    debugPrint("TaskLED start\n\r");
    bsp_queue_init(); // 初始化按键事件队列
    while (1)
    {
        status = xQueueReceive(gSWQueueHandle, &swevent.data, portMAX_DELAY);
        if (status == pdTRUE)
        {
            if (swevent.s.event)
            {
                // 处理按键事件
                SW_Handler(&swevent.s);
            }
        }
    }
}


int uart_printf(const char *fmt, ...)
{
    char fmtbuf[256]; // 注意栈溢出
    int cnt;
    va_list args;
    va_start(args, fmt);
    cnt = vsnprintf(fmtbuf, sizeof(fmtbuf), fmt, args);
    va_end(args);
    if (osMutexWait(usartMutexHandle, 1000) == osOK)
    {
        if (HAL_UART_Transmit(&huart1, (uint8_t *)fmtbuf, cnt, 1000) != HAL_OK)
        {
            cnt = -1;
        }
        osMutexRelease(usartMutexHandle);
    }
    else
    {
        cnt = -1;
    }
    return cnt;
}
